package com.dabai.iot.core.utils;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.dabai.common.core.utils.Func;
import com.dabai.iot.common.constant.IotKeyConstant;
import lombok.extern.slf4j.Slf4j;

import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 物模型消息解析工具类
 * 用于解析符合物模型规范的消息，将原始数据按功能模块和属性进行结构化处理
 *
 * @author kev1n
 */
@Slf4j
public class TslMessageParser {

    /**
     * 解析符合物模型规范的消息
     *
     * @param reportTime    消息上报时间
     * @param reportPayload 原始消息负载(JSON格式)
     * @param thingModelTsl 物模型TSL描述(JSON格式)
     * @return 结构化解析结果，包含匹配的功能模块及其属性
     */
    public static JSONObject parseThingModelMessage(Date reportTime, String reportPayload, String thingModelTsl) {
        // 提取消息中的功能模块数据
        Map<String, JSONObject> messageBlocks = extractMessageBlocks(reportPayload);
        // 提取 TSL 中的功能模块定义
        Map<String, JSONArray> tslBlocks = extractTslBlocks(thingModelTsl);

        // 找出消息和 TSL 中都存在的功能模块
        Set<String> commonBlockIds = findCommonKeys(messageBlocks.keySet(), tslBlocks.keySet());

        // 构建最终结果
        return buildResult(reportTime, commonBlockIds, messageBlocks, tslBlocks);
    }

    // ======================= 私有工具方法 ======================= //

    /**
     * 解析消息负载中的功能模块数据
     *
     * @param payload JSON格式的消息负载
     * @return 功能模块映射表(key : 模块标识符, value : 属性键值对)
     */
    private static Map<String, JSONObject> extractMessageBlocks(String payload) {
        JSONObject json = JSON.parseObject(payload);
        JSONArray blocks = json.getJSONArray(IotKeyConstant.BLOCKS);

        if (Func.isEmpty(blocks)) {
            return Collections.emptyMap();
        }

        return blocks.stream()
            .map(JSONObject.class::cast)
            .filter(blockJson -> blockJson.containsKey(IotKeyConstant.BLOCK_ID) && blockJson.containsKey(IotKeyConstant.PROPERTIES))
            .collect(Collectors.toMap(
                blockJson -> blockJson.getString(IotKeyConstant.BLOCK_ID),
                blockJson -> blockJson.getJSONObject(IotKeyConstant.PROPERTIES)
            ));
    }

    /**
     * 解析物模型TSL中的功能模块定义
     *
     * @param tsl JSON格式的物模型定义
     * @return TSL功能模块映射表(key : 模块标识符, value : 属性定义数组)
     */
    private static Map<String, JSONArray> extractTslBlocks(String tsl) {
        JSONObject tslJson = JSON.parseObject(tsl);
        JSONArray blocks = tslJson.getJSONArray(IotKeyConstant.BLOCKS);

        if (Func.isEmpty(blocks)) {
            return Collections.emptyMap();
        }

        return blocks.stream()
            .map(JSONObject.class::cast)
            .filter(blockJson -> blockJson.containsKey(IotKeyConstant.BLOCK_ID) && blockJson.containsKey(IotKeyConstant.PROPERTIES))
            .collect(Collectors.toMap(
                blockJson -> blockJson.getString(IotKeyConstant.BLOCK_ID),
                blockJson -> blockJson.getJSONArray(IotKeyConstant.PROPERTIES)
            ));
    }

    /**
     * 查找两个集合的交集
     *
     * @param keys1 第一个键集合
     * @param keys2 第二个键集合
     * @return 两个集合共有的键集合
     */
    private static Set<String> findCommonKeys(Set<String> keys1, Set<String> keys2) {
        return keys1.stream()
            .filter(keys2::contains)
            .collect(Collectors.toSet());
    }

    /**
     * 构建结构化解析结果
     *
     * @param reportTime     上报时间
     * @param commonBlockIds 共有的功能模块ID(blockIdentifier)集合
     * @param messageBlocks  消息中的功能模块数据
     * @param tslBlocks      TSL中的功能模块定义
     * @return 包含有效功能模块数据的JSON对象
     */
    private static JSONObject buildResult(Date reportTime,
                                          Set<String> commonBlockIds,
                                          Map<String, JSONObject> messageBlocks,
                                          Map<String, JSONArray> tslBlocks) {
        JSONArray blockArray = new JSONArray();

        commonBlockIds.forEach(blockIdentifier -> {
            JSONObject messageProps = messageBlocks.get(blockIdentifier);
            JSONArray tslProps = tslBlocks.get(blockIdentifier);

            JSONObject matchedProps = findMatchingProperties(messageProps, tslProps);

            if (!matchedProps.isEmpty()) {
                blockArray.add(new JSONObject()
                    .fluentPut(IotKeyConstant.BLOCK_ID, blockIdentifier)
                    .fluentPut(IotKeyConstant.PROPERTIES, matchedProps)
                    .fluentPut(IotKeyConstant.REPORT_TIME, reportTime.getTime())
                );
            }
        });

        return new JSONObject()
            .fluentPut(IotKeyConstant.BLOCKS, blockArray);
    }


    /**
     * 匹配消息属性与TSL定义的属性，并进行数据类型转换
     *
     * @param messageProps 消息中的属性键值对
     * @param tslProps     TSL中定义的属性数组
     * @return 在TSL中有定义且经过类型转换的属性集合
     */
    private static JSONObject findMatchingProperties(JSONObject messageProps, JSONArray tslProps) {
        // 创建TSL属性映射表：identifier -> 完整属性定义(包含dataType)
        Map<String, JSONObject> tslPropMap = tslProps.stream()
            .map(JSONObject.class::cast)
            .collect(Collectors.toMap(
                prop -> prop.getString(IotKeyConstant.IDENTIFIER),
                prop -> prop
            ));

        return messageProps.entrySet().stream()
            .filter(entry -> tslPropMap.containsKey(entry.getKey()))  // 过滤出TSL中定义的属性
            .collect(JSONObject::new,
                (obj, entry) -> {
                    String propId = entry.getKey();
                    Object value = entry.getValue();
                    JSONObject propDef = tslPropMap.get(propId);

                    // 获取TSL中定义的数据类型
                    String dataType = propDef.getString("dataType");

                    // 根据数据类型转换值
                    Object convertedValue = TslUtil.convertValue(value, dataType);
                    obj.put(propId, convertedValue);
                },
                JSONObject::putAll);
    }

    /**
     * 匹配消息属性与TSL定义的属性
     *
     * @param messageProps 消息中的属性键值对
     * @param tslProps     TSL中定义的属性数组
     * @return 在TSL中有定义的属性集合
     */
    private static JSONObject findMatchingPropertiesBackup(JSONObject messageProps, JSONArray tslProps) {
        Set<String> tslPropIdentifiers = tslProps.stream()
            .map(JSONObject.class::cast)
            .map(prop -> prop.getString(IotKeyConstant.IDENTIFIER))
            .collect(Collectors.toSet());

        return messageProps.entrySet().stream()
            .filter(entry -> tslPropIdentifiers.contains(entry.getKey()))
            .collect(JSONObject::new,
                (obj, entry) -> obj.put(entry.getKey(), entry.getValue()),
                JSONObject::putAll);
    }

}
